import java.io.*;
import java.util.StringTokenizer;
import java.lang.reflect.Array;

public class Assembler
{
	public static boolean movecheck(String command)
	{
	     return (command.equals("mfhi") || command.equals("mflo") || command.equals("mflow"));
	}
	public static boolean pseudocheck(String command)
	{
	     return (command.equals("div") || command.equals("divi") || command.equals("mul") || command.equals("muli") || command.equals("mult") || command.equals("multi"));
	}
//	checks to see if it is an rtype command returns true if it is
	public static boolean rcheck(String command)
	{
		return (command.equals("add") || command.equals("addu") || command.equals("sub") || command.equals("subu") || command.equals("or") || command.equals("and") || command.equals("slt") || command.equals("sltu") || command.equals("sllv") || command.equals("srlv") || command.equals("xor"));
	}
	public static boolean icheck(String command)
	{
		return (command.equals("addi") || command.equals("addiu") || command.equals("andi") || command.equals("ori") || command.equals("slti") || command.equals("sltiu") || command.equals("xori"));
	}
	//checks to see if it is a branch command returns true if it is
	public static boolean jcheck(String value)
	{
		return ((value.equals("beq")) || (value.equals("bne")) || (value.equals("j")) || (value.equals("jal")) || (value.equals("jr")) || value.equals("bge"));
	}
	public static int[] stringToInt(String string)
	{
		int size = string.length();
		int[] converted = new int[size + 1];
		converted[size] = 0;
		if(string.equals("\n"))
		{
		     converted = new int[2];
		     converted[0] = 12;
		     converted[1] = 0;
		     return converted;
		}
		for(int i = 0; i < size; ++i)
		{
			char character = string.charAt(i);
			converted[i] = (int)character;
			/*
			switch(character)
			{
			case '\n':
				converted[i] = 12;
			case ' ':
				converted[i] = 32;
				break;
			case 'a':
				converted[i] = 97;
				break;
			case 'A':
				converted[i] = 65;
				break;
			case 'b':
				converted[i] = 98;
				break;
			case 'B':
				converted[i] = 66;
				break;
			case 'c':
				converted[i] = 99;
				break;
			case 'C':
				converted[i] = 67;
				break;
			case 'd':
				converted[i] = 100;
				break;
			case 'D':
				converted[i] = 68;
				break;
			case 'e':
				converted[i] = 101;
				break;
			case 'E':
				converted[i] = 69;
				break;
			case 'f':
				converted[i] = 102;
				break;
			case 'F':
				converted[i] = 70;
				break;
			case 'g':
				converted[i] = 103;
				break;
			case 'G':
				converted[i] = 71;
				break;
			case 'h':
				converted[i] = 104;
				break;
			case 'H':
				converted[i] = 72;
				break;
			case 'i':
				converted[i] = 105;
				break;
			case 'I':
				converted[i] = 73;
				break;
			case 'j':
				converted[i] = 106;
				break;
			case 'J':
				converted[i] = 74;
				break;
			case 'k':
				converted[i] = 107;
				break;
			case 'K':
				converted[i] = 75;
				break;
			case 'l':
				converted[i] = 108;
				break;
			case 'L':
				converted[i] = 76;
				break;
			case 'm':
				converted[i] = 109;
				break;
			case 'M':
				converted[i] = 77;
				break;
			case 'n':
				converted[i] = 110;
				break;
			case 'N':
				converted[i] = 78;
				break;
			case 'o':
				converted[i] = 111;
				break;
			case 'O':
				converted[i] = 79;
				break;
			case 'p':
				converted[i] = 112;
				break;
			case 'P':
				converted[i] = 80;
				break;
			case 'q':
				converted[i] = 113;
				break;
			case 'Q':
				converted[i] = 81;
				break;
			case 'r':
				converted[i] = 114;
				break;
			case 'R':
				converted[i] = 82;
				break;
			case 's':
				converted[i] = 115;
				break;
			case 'S':
				converted[i] = 83;
				break;
			case 't':
				converted[i] = 116;
				break;
			case 'T':
				converted[i] = 84;
				break;
			case 'u':
				converted[i] = 117;
				break;
			case 'U':
				converted[i] = 85;
				break;
			case 'v':
				converted[i] = 118;
				break;
			case 'V':
				converted[i] = 86;
				break;
			case 'w':
				converted[i] = 119;
				break;
			case 'W':
				converted[i] = 87;
				break;
			case 'x':
				converted[i] = 120;
				break;
			case 'X':
				converted[i] = 88;
				break;
			case 'y':
				converted[i] = 121;
				break;
			case 'Y':
				converted[i] = 89;
				break;
			case 'z':
				converted[i] = 122;
				break;
			case 'Z':
				converted[i] = 90;
				break;
			case '!':
			     converted[i] = 33;
			     break;
			case '\"':
			     converted[i] = 34;
			     break;
			case '#':
			     converted[i] = 35;
			     break;
			case '$':
			     converted[i] = 36;
			     break;
			case '%':
			     converted[i] = 37;
			     break;
			case '&':
			     converted[i] = 38;
			     break;
			case '\'':
			     converted[i] = 39;
			     break;
			case '(':
			     converted[i] = 40;
			     break;
			case ')':
			     converted[i] = 41;
			     break;
			case '*':
			     converted[i] = 42;
			     break;
			case '+':
			     converted[i] = 43;
			     break;
			case ',':
			     converted[i] = 44;
			     break;
			case '-':
			     converted[i] = 45;
			     break;
			case '.':
			     converted[i] = 46;
			     break;
			case '/':
			     converted[i] = 47;
			     break;
			case '0':
			     converted[i] = 48;
			     break;
			case '1':
			     converted[i] = 49;
			     break;
			case '2':
			     converted[i] = 50;
			     break;
			case '3':
			     converted[i] = 51;
			     break;
			case '4':
			     converted[i] = 52;
			     break;
			case '5':
			     converted[i] = 53;
			     break;
			case '6':
			     converted[i] = 54;
			     break;
			case '7':
			     converted[i] = 55;
			     break;
			case '8':
			     converted[i] = 56;
			     break;
			case '9':
			     converted[i] = 57;
			     break;
			case ':':
			     converted[i] = 58;
			     break;
			case ';':
			     converted[i] = 59;
			     break;
			case '<':
			     converted[i] = 60;
			     break;
			case '=':
			     converted[i] = 61;
			     break;
			case '>':
			     converted[i] = 62;
			     break;
			case '?':
			     converted[i] = 63;
			     break;
			case '@':
			     converted[i] = 64;
			     break;
			case '{':
			     converted[i] = 123;
			     break;
			case '|':
			     converted[i] = 124;
			     break;
			case '}':
			     converted[i] = 125;
			     break;
			case '~':
			     converted[i] = 126;
			     break;
			case '`':
			     converted[i] = 96;
			     break;
			}
			*/
			
		}
		return converted;
	}
		//analyizes a line to see if anything should be placed in the label table
	public static String linecheck(String line, String endofline)
	{
		StringTokenizer tokenizer = new StringTokenizer(line);
		String garbage = new String(tokenizer.nextToken());
		String returnValue = "";
		
		if(garbage.equals(endofline))
		     return "\n";
		
		if(tokenizer.hasMoreTokens())
		{
			garbage = tokenizer.nextToken();
			if(garbage.charAt(0) == '.')
				while(tokenizer.hasMoreTokens())
					returnValue = returnValue + " " + tokenizer.nextToken();
		}
		return returnValue;
	}
	//analyizes a line to see if anything should be placed in the label table
	public static boolean intCheck(String line)
	{
	     StringTokenizer tokenizer = new StringTokenizer(line);
	     String garbage = tokenizer.nextToken();
	     String type;
	     
	     if(!tokenizer.hasMoreTokens())
		  return false;
	     
	     type = tokenizer.nextToken();
	     if((type.equalsIgnoreCase(".integer") || type.equalsIgnoreCase(".int")) && tokenizer.hasMoreTokens())
		       return true;
	     
	     return false;
	}
	public static int getInt(String line)
	{
	     StringTokenizer tokenizer = new StringTokenizer(line);
	     String garbage = tokenizer.nextToken();
	     int integer;
	     
	     garbage = tokenizer.nextToken();
	     integer = Integer.parseInt(tokenizer.nextToken());
	     
	     return integer;
	}
	public static boolean floatCheck(String line)
	{
	     StringTokenizer tokenizer = new StringTokenizer(line);
	     String garbage = tokenizer.nextToken();
	     String type;
	     
	     if(!tokenizer.hasMoreTokens())
		  return false;
	     
	     type = tokenizer.nextToken();
	     if(type.equalsIgnoreCase(".float") && tokenizer.hasMoreTokens())
		       return true;
	     
	     return false;
	}
	public static int getFloat(String line)
	{
	     StringTokenizer tokenizer = new StringTokenizer(line);
	     String garbage = tokenizer.nextToken();
	     float fp;
	     
	     garbage = tokenizer.nextToken();
	     fp = Float.parseFloat(tokenizer.nextToken());
	     
	     return Float.floatToRawIntBits(fp);
	}
	public static int[] getLong(String line)
	{
	     StringTokenizer tokenizer = new StringTokenizer(line);
	     String garbage = tokenizer.nextToken();
	     String part1, part2;
	     int i = 0;
	     int size;
	     long longValue;
	     int[] values = new int[2];
	     
	     garbage = tokenizer.nextToken();
	     longValue = Long.parseLong(tokenizer.nextToken());
	     
	     garbage = Long.toBinaryString(longValue);
	     
	     size = garbage.length();
	     
	     if(size == 64)
	     {
		  part1 = garbage.substring(0, 32);
		  part2 = garbage.substring(32, 64);
	     }
	     else
	     {
		  part1 = "";
		  part2 = "";
		  
		  while(i < 32 && i < size)
		  {
		       part1 = garbage.charAt(size - i - 1) + part1;
		       ++i;
		  }
		  
		  if(i >= size)
		       part2 = "0";
		  
		  while(i < size)
		  {
		       part2 = garbage.charAt(size - i - 1) + part2;
		       ++i;
		  }
	     }
	     
	     values[0] = Integer.parseInt(part1, 2);
	     values[1] = Integer.parseInt(part2, 2);
	     
	     return values;
	}
public static boolean doubleCheck(String line)
	{
	     StringTokenizer tokenizer = new StringTokenizer(line);
	     String garbage = tokenizer.nextToken();
	     String type;
	     
	     if(!tokenizer.hasMoreTokens())
		  return false;
	     
	     type = tokenizer.nextToken();
	     if(type.equalsIgnoreCase(".double") && tokenizer.hasMoreTokens())
		       return true;
	     
	     return false;
	}
	public static int[] getDouble(String line)
	{
	     StringTokenizer tokenizer = new StringTokenizer(line);
	     String garbage = tokenizer.nextToken();
	     String part1, part2;
	     int i = 0;
	     int size;
	     double doubleValue;
	     long longValue;
	     int[] values = new int[2];
	     
	     garbage = tokenizer.nextToken();
	     doubleValue = Double.parseDouble(tokenizer.nextToken());
	     
	     longValue = Double.doubleToRawLongBits(doubleValue);
	     
	     garbage = Long.toBinaryString(longValue);
	     
	     size = garbage.length();
	     
	     if(size == 64)
	     {
		  part1 = garbage.substring(0, 32);
		  part2 = garbage.substring(32, 64);
	     }
	     else
	     {
		  part1 = "";
		  part2 = "";
		  
		  while(i < 32 && i < size)
		  {
		       part1 = garbage.charAt(size - i - 1) + part1;
		       ++i;
		  }
		  
		  if(i >= size)
		       part2 = "0";
		  
		  while(i < size)
		  {
		       part2 = garbage.charAt(size - i - 1) + part2;
		       ++i;
		  }
	     }
	     
	     values[0] = Integer.parseInt(part1, 2);
	     values[1] = Integer.parseInt(part2, 2);
	     
	     return values;
	}
	public static String linecheck(String line)
	{
		String string = "";
                int i = 0;
		int length = line.length();
		
		while(i < length && line.charAt(i) != '.')
		     ++i;
		
		++i;
		if(i >= length || line.charAt(i) != 'a')
		     return "";
		
		++i;
		if(i >= length || line.charAt(i) != 's')
		     return "";
		
		++i;
		if(i >= length || line.charAt(i) != 'c')
		     return "";
		
		++i;
		if(i >= length || line.charAt(i) != 'i')
		     return "";
		
		++i;
		if(i >= length || line.charAt(i) != 'i')
		     return "";
		
		++i;
		if(i >= length || line.charAt(i) != 'z')
		     return "";
		
		while(i < length && line.charAt(i) != '\"')
		     ++i;
		
		if(i == length)
		     return "";
		
		++i;
		
		while(i < length && line.charAt(i) != '\"')
		{
		     if(line.charAt(i) == '\\')
		     {
			  ++i;
			  if(i >= length)
			       return "";
			  
			  switch(line.charAt(i))
			  {
			  case 'n':
			       string = string + '\n';
			       break;
			  case 't':
			       string = string + '\t';
			       break;
			  case '\\':
			       string = string + '\\';
			       break;
			  case '\'':
			       string = string + '\'';
			       break;
			  case '\"':
			       string = string + '\"';
			       break;
			  case '#':
			       string = string + '#';
			       break;
			  }
		     }
		     else
			  string = string + line.charAt(i);
		     
		     ++i;
		}
		
		return string;
	}
	
	public static Assembled assemble(String inputFile) throws IOException
	{
		String temp;
		String value;
		
		Assembled assembled = new Assembled();
		int linecounter = 1;
		int pc = 0;

		File f = new File(inputFile);
		FileReader fr = new FileReader(f);
		BufferedReader br = new BufferedReader(fr);

		StringTokenizer tokenizer;
				
		temp = br.readLine();
		
		//this is the first pass for the reading of the file
		//in this pass all the label positions will be determined
		//and they will be stored in the datatable array
		//this will be the first thing that the simulator will get to see
		while(temp != null)//makes sure that the first line isnt null indicating a blank file
		{
			if(!(temp.equals("\n"))) //ignores any blank lines
			{
				temp = temp.trim(); //trims the data so that it can be used properly
			
				if((temp.length() > 0) && temp.charAt(0) != '#' && temp.charAt(0) != '.') //makes sure that it isnt a comment
				{
					tokenizer = new StringTokenizer(temp);
					if(tokenizer.hasMoreTokens())
					{
						String line = tokenizer.nextToken(); //checks the first item in the line
						line = line.trim(); //trims that data so that i can check to see if it is a label
						
						if(line.charAt(line.length() - 1) == ':') //checks for labelhood
						{
							//creates label data and increments the labelcounter
							if(!linecheck(temp).equals(""))
							{
								assembled.addMemory(line.substring(0, line.length() - 1), stringToInt(linecheck(temp)));
							}
							else
							{
								assembled.addLabel(line.substring(0, line.length() - 1), linecounter);
							}
							if(tokenizer.hasMoreTokens())
							{
								temp = tokenizer.nextToken();
								temp = temp.trim();
								if(rcheck(temp) || icheck(temp) || jcheck(temp) || temp.equals("lw") || temp.equals("sw") || temp.equals("la") || temp.equals("li") || temp.equals("move"))
								{
									if(linecounter == 0)
										++linecounter;
								}
								else
									--linecounter;
							}
							else
								--linecounter;
						}
					}
					++linecounter;
				}

				temp = br.readLine();
			}
		}//end first pass
		
		br.close();
	
		fr = new FileReader(f);
		br = new BufferedReader(fr);
		
		temp = br.readLine();
		
		linecounter = 1;

		//the second pass converts all the data to something the simulator will understand
		while(temp != null)
		{
			//gets what is potentially the next command
			tokenizer = new StringTokenizer(temp);
			if(tokenizer.hasMoreTokens())
			{
				//checks to see if the line has a label in front of it
				//if it does then it reconstructs the line with no label
				value = tokenizer.nextToken();
				if(value.charAt(value.length() - 1) == ':' && tokenizer.hasMoreTokens())
				{
					String newtemp;
					value = tokenizer.nextToken();
					
					if(value.charAt(0) != '.')
					{
						newtemp = value + " ";
						while(tokenizer.hasMoreTokens())
							newtemp = newtemp + ", " + tokenizer.nextToken();
						
						temp = newtemp;
					}
					
				}
				
				//checks for rtype and generates propper output if it is
				if(rcheck(value))
				{
					RType.convert(temp, linecounter, assembled);
					++pc;
				}
				else if(value.equals("move"))
				{
					RType.moveconvert(temp, linecounter, assembled);
					++pc;
				}
				else if(value.equals("li"))
				{
					IType.liconvert(temp, linecounter, assembled);
					++pc;
				}
				//checks for immediate type and generates output if it is
				else if(icheck(value))
				{
					IType.convert(temp, linecounter, assembled);
					++pc;
				}
				//checks for branching type and generates the output if it is
				else if(jcheck(value))
				{
					Branch.convert(temp, linecounter, assembled);
					++pc;
				}
				//checks for load/store operations and creates that output
				else if(value.equals("lw") || value.equals("sw") || value.equals("la"))
				{
					LSType.convert(temp, linecounter, assembled);
					++pc;
				}
				//checks for syscall and generates the output as necessary
				else if(value.equals("syscall"))
				{
					int[] build = new int[8];
					build[0] = 4;
					build[1] = 0;
					build[2] = 4;
					build[3] = 0;
					build[4] = 2;
					build[5] = 0;
					build[6] = 12;
					build[7] = linecounter;
					
					assembled.addLine(build);
					
					++pc;
				}
				else if(value.equals("endprogram"))
				{
					int[] build = new int[3];
					build[0] = 5;
					build[1] = 64;
					build[2] = linecounter;
					
					assembled.addLine(build);
					
					++pc;
				}
				else if(value.endsWith("double"))
				{
				     
				}
				else if(value.endsWith("float"))
				{
				     
				}
				else if(value.endsWith("long"))
				{
				     
				}
				else if(movecheck(value))
				{
				     MoveType.convert(temp, linecounter, assembled);
				}
				else if(pseudocheck(value))
				{
				     PseudoType.convert(temp, linecounter, assembled);
				}
				else if(!value.trim().startsWith("#") && !value.trim().startsWith("."))
				{
				     assembled.addError("Error on line " + linecounter + "\nUnrecognized command " + temp);
				}
			}
			
			//reads next line
			temp = br.readLine();
			++linecounter;
		}
		
		return assembled;
	}
	public static Assembled assemble(String inputFile, String endOfLineLabel)
	throws IOException
	{
		String temp;
		String value;
		
		Assembled assembled = new Assembled();
		int linecounter = 1;
		int pc = 0;

		File f = new File(inputFile);
		FileReader fr = new FileReader(f);
		BufferedReader br = new BufferedReader(fr);

		StringTokenizer tokenizer;
				
		temp = br.readLine();
		
		assembled.addError("Warning, using a deprecated assembler.");
		
		//this is the first pass for the reading of the file
		//in this pass all the label positions will be determined
		//and they will be stored in the datatable array
		//this will be the first thing that the simulator will get to see
		while(temp != null)//makes sure that the first line isnt null indicating a blank file
		{
			if(!(temp.equals("\n"))) //ignores any blank lines
			{
				temp = temp.trim(); //trims the data so that it can be used properly
			
				if((temp.length() > 0) && temp.charAt(0) != '#' && temp.charAt(0) != '.') //makes sure that it isnt a comment
				{
					tokenizer = new StringTokenizer(temp);
					if(tokenizer.hasMoreTokens())
					{
						String line = tokenizer.nextToken(); //checks the first item in the line
						line = line.trim(); //trims that data so that i can check to see if it is a label
						
						if(line.charAt(line.length() - 1) == ':') //checks for labelhood
						{
						     String checkedLine = linecheck(temp, endOfLineLabel);
							//creates label data and increments the labelcounter
							if(!checkedLine.equals(""))
							{
								assembled.addMemory(line.substring(0, line.length() - 1), stringToInt(checkedLine));
							}
							else
							{
								assembled.addLabel(line.substring(0, line.length() - 1), linecounter);
							}
							if(tokenizer.hasMoreTokens())
							{
								temp = tokenizer.nextToken();
								temp = temp.trim();
								if(rcheck(temp) || icheck(temp) || jcheck(temp) || temp.equals("lw") || temp.equals("sw") || temp.equals("la") || temp.equals("li") || temp.equals("move"))
								{
									if(linecounter == 0)
										++linecounter;
								}
								else
									--linecounter;
							}
							else
								--linecounter;
						}
					}
					++linecounter;
				}

				temp = br.readLine();
			}
		}//end first pass
		
		br.close();
	
		fr = new FileReader(f);
		br = new BufferedReader(fr);
		
		temp = br.readLine();
		
		linecounter = 1;

		//the second pass converts all the data to something the simulator will understand
		while(temp != null)
		{
			//gets what is potentially the next command
			tokenizer = new StringTokenizer(temp);
			if(tokenizer.hasMoreTokens())
			{
				//checks to see if the line has a label in front of it
				//if it does then it reconstructs the line with no label
				value = tokenizer.nextToken();
				if(value.charAt(value.length() - 1) == ':' && tokenizer.hasMoreTokens())
				{
					String newtemp;
					value = tokenizer.nextToken();
					
					if(value.charAt(0) != '.')
					{
						newtemp = value + " ";
						while(tokenizer.hasMoreTokens())
							newtemp = newtemp + ", " + tokenizer.nextToken();
						
						temp = newtemp;
					}
					
				}
				
				//checks for rtype and generates propper output if it is
				if(rcheck(value))
				{
					RType.convert(temp, linecounter, assembled);
					++pc;
				}
				else if(value.equals("move"))
				{
					RType.moveconvert(temp, linecounter, assembled);
					++pc;
				}
				else if(value.equals("li"))
				{
					IType.liconvert(temp, linecounter, assembled);
					++pc;
				}
				//checks for immediate type and generates output if it is
				else if(icheck(value))
				{
					IType.convert(temp, linecounter, assembled);
					++pc;
				}
				//checks for branching type and generates the output if it is
				else if(jcheck(value))
				{
					Branch.convert(temp, linecounter, assembled);
					++pc;
				}
				//checks for load/store operations and creates that output
				else if(value.equals("lw") || value.equals("sw") || value.equals("la"))
				{
					LSType.convert(temp, linecounter, assembled);
					++pc;
				}
				//checks for syscall and generates the output as necessary
				else if(value.equals("syscall"))
				{
					int[] build = new int[8];
					build[0] = 4;
					build[1] = 0;
					build[2] = 4;
					build[3] = 0;
					build[4] = 2;
					build[5] = 0;
					build[6] = 12;
					build[7] = linecounter;
					
					assembled.addLine(build);
					
					++pc;
				}
				else if(value.equals("endprogram"))
				{
					int[] build = new int[3];
					build[0] = 5;
					build[1] = 64;
					build[2] = linecounter;
					
					assembled.addLine(build);
					
					++pc;
				}
				else
				{
				     assembled.addError("Error on line " + linecounter);
				     assembled.addError("Unreconized command " + temp);
				}
			}
			
			//reads next line
			temp = br.readLine();
			++linecounter;
		}
		
		return assembled;
	}
}